<?php

namespace Core;

use DI\Container;
use DI\ContainerBuilder;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;

class BeanFactory
{
    private static $env = []; //配置
    /**@var $container Container** */
    private static $container; //IOC 容器
    private static $annotationHandlers;

    /**
     * 获取配置值
     * @param string $key
     * @param string $default
     * @return mixed|string
     */
    private static function getEnv(string $key, string $default = "")
    {
        if (isset(self::$env[$key]) && self::$env[$key]) {
            return self::$env[$key];
        }
        return $default;
    }

    /**
     * 加载注解处理方法
     */
    private static function loadAnnotationHandlers()
    {
        $handler = [];
        $handlerFiles = glob(ROOT_PATH . "/core/annotationhandlers/*.php");
        foreach ($handlerFiles as $file) {
            $handler = array_merge($handler, require_once($file));
        }
        self::$annotationHandlers = $handler;
    }

    /**
     * bean容器初始化
     * @throws \DI\DependencyException
     * @throws \DI\NotFoundException
     * @throws \Doctrine\Common\Annotations\AnnotationException
     * @throws \ReflectionException
     */
    public static function init()
    {
        //获取配置文件
        $config = parse_ini_file(ROOT_PATH . "/env", true);
        self::$env = $config;
        $builder = new ContainerBuilder(); //初始化容器builder
        $builder->useAnnotations(true); //启用注解  主要适用他的Inject注解
        self::$container = $builder->build();//容器初始化
        //加载注解处理handler
        self::loadAnnotationHandlers();
        //设置注解加载类
//        AnnotationRegistry::registerAutoloadNamespace("Core\annotations");
        $load = require(ROOT_PATH . "/vendor/autoload.php");
        AnnotationRegistry::registerLoader([$load, "loadClass"]);
        //扫描需要注入容器的 文件夹
        $scans = [
            ROOT_PATH . "/core/init" => "Core\\",
            self::getEnv("scan_dir", ROOT_PATH . "/app") => self::getEnv("scan_root_namespace", "App\\")
        ];
        foreach ($scans as $scan_dir => $namespace) {
            self::ScanBeans($scan_dir, $namespace);
        }
    }

    /**
     * 获取容器所有需要注入的文件
     * @param $dir
     * @return array
     */
    private static function getAllBeanFiles($dir)
    {
        $ret = [];
        $files = glob($dir . "/*");
        foreach ($files as $file) {
            if (is_dir($file)) {
                $ret = array_merge($ret, self::getAllBeanFiles($file));
            } elseif (pathinfo($file)['extension'] == "php") {
                $ret[] = $file;
            }
        }
        return $ret;
    }

    /**
     * 扫描对应的文件夹 通过注解设置类  并绑定到容器内
     * @param string $dir
     * @param string $namespace
     * @throws \DI\DependencyException
     * @throws \DI\NotFoundException
     * @throws \Doctrine\Common\Annotations\AnnotationException
     * @throws \ReflectionException
     */
    public static function ScanBeans(string $dir, string $namespace)
    {
        $all_files = self::getAllBeanFiles($dir);
        foreach ($all_files as $file) {
            require_once($file);
        }
        $reader = new AnnotationReader();
        // 返回由已定义类的名字所组成的数组
        foreach (get_declared_classes() as $class) {
            if (strstr($class, $namespace)) {
                //获取反射类
                $ref_class = new \ReflectionClass($class);
                //获取类注解
                $class_annos = $reader->getClassAnnotations($ref_class);
                foreach ($class_annos as $anno) {
                    $anno_class = get_class($anno);
                    $anno_handlers = self::$annotationHandlers;
                    if (!isset($anno_handlers[$anno_class])) {
                        throw new \Exception("未设置容器{$anno_class}-handler");
                    }
                    $handler = $anno_handlers[$anno_class];
                    //self::$container->get($ref_class->getName()  容器内并未有$ref_class->getName(),是采用内部机制创建实例并绑定到容器
                    $instance = self::$container->get($ref_class->getName());
                    //属性注解处理
                    self::propAnnotationHandler($instance, $ref_class, $reader);
                    //方法注解
                    self::methodAnnotationHandler($instance, $ref_class, $reader);
                    //handler($instance,$container,$bean)   执行类注解相关操作   类注解有设置别名 则 将别名作为键名绑定到容器 反之 则绑定短名称到容器
                    $handler($instance, self::$container, $anno);
                }
            }
        }
    }

    /**
     * 属性注解类
     * @param $instance [实例化对象]
     * @param \ReflectionClass $class [反射对象]
     * @param AnnotationReader $reader [注解读取类]
     * @throws \Exception
     */
    public static function propAnnotationHandler(&$instance, \ReflectionClass $class, AnnotationReader $reader)
    {
        $handler = self::$annotationHandlers;
        //获取所有属性
        $propers = $class->getProperties();
        foreach ($propers as $proper) {
            //获取属性注解
            $annos = $reader->getPropertyAnnotations($proper);
            foreach ($annos as $anno) {
                if (!isset($handler[get_class($anno)])) {
                    continue;
                }
                $doHandler = $handler[get_class($anno)];
                $doHandler($proper, $instance, $anno);
            }
        }
    }

    /**
     * 方法注解处理
     * @param $instance [实例化对象]
     * @param \ReflectionClass $class [对象反射类]
     * @param AnnotationReader $reader [注解读取类]
     * @throws \Exception
     */
    public static function methodAnnotationHandler(&$instance, \ReflectionClass $class, AnnotationReader $reader)
    {
        $handler = self::$annotationHandlers;
        //获取所有方法
        $methods = $class->getMethods();
        foreach ($methods as $method) {
            //获取方法注解
            $annos = $reader->getMethodAnnotations($method);
            foreach ($annos as $anno) {
                if (!isset($handler[get_class($anno)])) {
                    throw new \Exception("未设置方法注解 处理方式");
                }
                $doHandler = $handler[get_class($anno)];
                $doHandler($method, $instance, $anno);
            }
        }
    }

    /**
     * 获取从容器获取类
     * @param string $name
     * @return mixed
     * @throws \DI\DependencyException
     * @throws \DI\NotFoundException
     */
    public static function getBean(string $name)
    {
        //调用不存在的类时报错,返回false
        try {
            return self::$container->get($name);
        } catch (\Exception $exception) {
            return false;
        }
    }

    /**
     * 注入容器
     * @param string $name
     * @param $fn
     */
    public static function setBean(string $name, $fn)
    {
        return self::$container->set($name, $fn);
    }

}
